1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.util.system;
12 
13 import hip.util.conv;
14 import hip.util.string:fromStringz, toStringz;
15 import hip.util.path:pathSeparator;
16 
17 version(PSVita) version = NoSharedLibrarySupport;
18 version(WebAssembly) version = NoSharedLibrarySupport;
19 version(CustomRuntimeTest) version = NoSharedLibrarySupport;
20 
21 debug version(Windows)
22 {
23     pragma(lib, "psapi.lib");
24     pragma(lib, "dbghelp.lib");
25     private struct MODLOAD_DATA {DWORD ssize; DWORD ssig; PVOID data; DWORD size; DWORD flags; }
26     import core.sys.windows.windef;
27     extern(Windows) private 
28     {
29         alias SymUnloadModule = SymUnloadModule64;
30         BOOL SymUnloadModule64(HANDLE hProcess, DWORD64 BaseOfDll);
31         DWORD64 SymLoadModuleEx(
32             HANDLE        hProcess,
33             HANDLE        hFile,
34             PCSTR         ImageName,
35             PCSTR         ModuleName,
36             DWORD64       BaseOfDll,
37             DWORD         DllSize,
38             MODLOAD_DATA* Data,
39             DWORD         Flags
40         );
41     }
42 }
43 enum debugger = "asm {int 3;}";
44 
45 char[] sanitizePath(string path) @safe pure nothrow
46 {
47     char[] ret = new char[](path.length);
48 
49     foreach(i, c; path)
50     {
51         version(Windows)
52         {
53             if(c == '/')
54                 ret[i] = '\\';
55             else
56                 ret[i] = c;
57         }
58         else
59         {
60             if(c == '\\')
61                 ret[i] = '/';
62             else
63                 ret[i] = c;
64         }
65     }
66     return ret;
67 }
68 bool isPathUnixStyle(string path) @safe pure nothrow 
69 {
70     for(size_t i = 0; i < path.length; i++)
71         if(path[i] == '/')
72             return true;
73     return false;
74 }
75 string buildPath(string[] args...) @safe pure nothrow
76 {
77     if(args.length == 0)
78         return null;
79     string ret;
80     for(int i = 0; i < cast(int)args.length-1; i++)
81         ret~= args[i]~pathSeparator;
82     return ret~args[$-1];
83 }
84 
85 version(Windows)
86 {
87     // import core.sys.windows.winbase;
88     // import core.sys.windows.windef;
89 
90     import hip.util.windows;
91 
92     private HMODULE moduleHandle;
93     extern(Windows) nothrow @system void* dll_import_var(string name)
94     {
95         if(moduleHandle == null)
96             moduleHandle = GetModuleHandle(null);
97         return GetProcAddress(moduleHandle, (name~"\0").ptr);
98     }
99     string[] dllImportVariables(Args...)()
100     {
101         import std.traits:isFunctionPointer;
102         string[] failedFunctions;
103         static foreach(a; Args)
104         {
105             static assert(isFunctionPointer!a, "Can't dll import a non function pointer ( "~a.stringof~" )");
106             a = cast(typeof(a))dll_import_var(a.stringof);
107             if(a is null)
108                 failedFunctions~= a.stringof;
109         }
110         return failedFunctions;
111     }
112 
113     string getWindowsErrorMessage(HRESULT hr)
114     {
115         wchar* buffer;
116         HRESULT fmt = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | 
117         FORMAT_MESSAGE_IGNORE_INSERTS |
118         FORMAT_MESSAGE_ALLOCATE_BUFFER,
119         null, hr, 0u, cast(LPWSTR)&buffer, 0, null);
120 
121         if(fmt == 0)
122             return "Error code '"~hip.util.conv.toString(hr)~"' not found";
123         string ret = fromUTF16(cast(wstring)buffer[0..fmt]);
124 
125         LocalFree(buffer);
126 
127         return ret;
128     }
129 
130     void winEnforce(scope BOOL delegate() dg, scope string message)
131     {
132         if(!dg())
133             throw new Error(message~": "~getWindowsErrorMessage(GetLastError()));
134     }
135 }
136 
137 
138 string dynamicLibraryGetLibName(string libName)
139 {
140     version(NoSharedLibrarySupport) return "";
141     else version(Windows) return libName~".dll";
142     else version(Posix)
143     {
144         import hip.util.path;
145         libName.filename = "lib"~libName.filename~".so";
146         return libName;
147     }
148     else static assert(0, "Platform not supported");
149 }
150 
151 bool dynamicLibraryIsLibNameValid(string libName)
152 {
153     version(NoSharedLibrarySupport)
154         return true;
155     else version(Windows)
156         return libName[$-4..$] == ".dll";
157     else version(Posix)
158     {
159         import hip.util.path;
160         return libName.filename[0..3] == "lib" && libName[$-3..$] == ".so";
161     }
162 }
163 
164 ///It will open the current executable if libName == null
165 void* dynamicLibraryLoad(string libName)
166 {
167     void* ret;
168     if(libName == null)
169     {
170         version(NoSharedLibrarySupport)
171             ret = null;
172         else version(Windows)
173         {
174             ret = GetModuleHandle(null);
175         }
176         else version(Posix)
177         {
178             import core.sys.posix.dlfcn : dlopen, RTLD_NOW;
179             ret = dlopen(null, RTLD_NOW);
180         }
181     }
182     else
183     {
184         version(NoSharedLibrarySupport)
185             ret = null;
186         else version(Android)
187         {
188             import core.sys.posix.dlfcn : dlopen, RTLD_LAZY;
189             ret = dlopen(libName.toStringz, RTLD_LAZY);
190         }
191         else version(Windows)
192         {
193             import core.runtime;
194             ret = Runtime.loadLibrary(libName);
195             debug
196             {
197                 import core.sys.windows.psapi;
198                 import core.sys.windows.winbase;
199                 MODULEINFO moduleInfo;
200 	            winEnforce(() => GetModuleInformation(GetCurrentProcess(), ret, &moduleInfo, MODULEINFO.sizeof), "Could not get module information");
201                 if (!SymLoadModuleEx(GetCurrentProcess(), null, (libName~'\0').ptr, null, cast(ulong)moduleInfo.lpBaseOfDll, moduleInfo.SizeOfImage, null, 0))
202                 {
203                     throw new Error("Failed to load the DLL named "~libName~" pdb symbols");
204                 }
205                 
206             }
207         }
208         else version(Posix)
209         {
210             import core.sys.posix.dlfcn : dlopen, RTLD_LAZY;
211             ret = dlopen(libName.toStringz, RTLD_LAZY);
212         }
213     }
214     return ret;
215 }
216 
217 version(Windows) private const (char)* err;
218 void* dynamicLibrarySymbolLink(void* dll, const (char)* symbolName)
219 {
220     void* ret;
221     version(NoSharedLibrarySupport)
222         ret = null;
223     else version(Windows)
224     {
225         ret = GetProcAddress(dll, symbolName);
226         if(!ret)
227             err = ("Could not link symbol "~symbolName.fromStringz).ptr;
228     }
229     else version(Posix)
230     {
231         import core.sys.posix.dlfcn : dlsym;
232         ret = dlsym(dll, symbolName);
233     }
234     return ret;
235 }
236 
237 
238 string dynamicLibraryError()
239 {
240     version(NoSharedLibrarySupport)
241         return "Current platform does not load dynamic libraries";
242     else version(Windows)
243     {
244         const(char)* ret = err;
245         err = null;
246         return cast(string)fromStringz(ret);
247     }
248     else version(Posix)
249     {
250         import core.sys.posix.dlfcn;
251         return cast(string)fromStringz(dlerror());
252     }
253     else static assert(0, "Platform not supported");
254 }
255 
256 bool dynamicLibraryRelease(void* dll)
257 {
258     version(NoSharedLibrarySupport)
259         return false;
260     else version(UWP)
261     {
262         return cast(bool)FreeLibrary(dll);
263     }
264     else version(Android)
265     {
266         import core.sys.posix.dlfcn:dlclose;
267         return cast(bool)dlclose(dll);
268     }
269     else version(Posix)
270     {
271         import core.sys.posix.dlfcn:dlclose;
272         return cast(bool)dlclose(dll);
273     }
274     else
275     {
276         import core.runtime;
277         debug version(Windows)
278         {
279             import core.sys.windows.winbase;
280             import core.sys.windows.psapi;
281             MODULEINFO moduleInfo;
282             winEnforce(() => GetModuleInformation(GetCurrentProcess(), dll, &moduleInfo, MODULEINFO.sizeof), "Could not get module information");
283             winEnforce(() => SymUnloadModule(GetCurrentProcess(), cast(ulong)moduleInfo.lpBaseOfDll), "Could not unload PDB");
284         }
285         return Runtime.unloadLibrary(dll);
286     }
287 }